package com.candy.common.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.candy.common.entity.CommonTableInfo;
import com.candy.common.exception.BusinessException;
import com.candy.common.mapper.CommonMapper;
import com.candy.common.service.ICommonService;
import com.google.common.base.CaseFormat;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>
 * 通用表单 服务实现类
 * </p>
 *
 * @author pxs
 * @since 2021-12-13
 */
@Slf4j
@Service
public class CommonServiceImpl implements ICommonService
{

    private static final Map<String, CommonTableInfo> TABLE_CACHE = new ConcurrentHashMap();

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    @Autowired
    private DataSource dataSource;

    @Resource
    private CommonMapper commonMapper;


    @Override
    public boolean saveMap(HashMap<String, Object> entity, String tableName)
    {
        CommonTableInfo commonTableInfo = getCommonTableInfoByTableName(tableName);
        HashMap<String, Object> dbEntity = getDBEntity(commonTableInfo.getPropertyAndColumnMap(), entity);
        if (dbEntity.isEmpty())
        {
            log.error("参数为空");
            return false;
        }
        return SqlHelper.retBool(commonMapper.saveMap(dbEntity, tableName));
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public boolean saveMapBatch(Collection<HashMap<String, Object>> entityList, String tableName)
    {
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        CommonMapper mapper = sqlSession.getMapper(CommonMapper.class);
        CommonTableInfo commonTableInfo = getCommonTableInfoByTableName(tableName);
        try
        {
            for (HashMap<String, Object> entity : entityList)
            {
                HashMap<String, Object> dbEntity = getDBEntity(commonTableInfo.getPropertyAndColumnMap(), entity);
                mapper.saveMap(dbEntity, tableName);
            }
            sqlSession.commit();
            //清理缓存，防止溢出
            sqlSession.clearCache();
            return true;
        } catch (Exception e) {
            //异常回滚
            sqlSession.rollback();
            return false;
        } finally {
            sqlSession.close();
        }
    }

    @Override
    public boolean updateMapById(HashMap<String, Object> entity, String tableName)
    {
        CommonTableInfo commonTableInfo = getCommonTableInfoByTableName(tableName);
        HashMap<String, Object> dbEntity = getDBEntity(commonTableInfo.getPropertyAndColumnMap(), entity);
        return SqlHelper.retBool(commonMapper.updateMapById(dbEntity, tableName));
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public boolean updateMapBatchById(Collection<HashMap<String, Object>> entityList, String tableName)
    {
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        CommonMapper mapper = sqlSession.getMapper(CommonMapper.class);
        CommonTableInfo commonTableInfo = getCommonTableInfoByTableName(tableName);
        try
        {
            HashMap<String, String> propertyAndColumnMap = commonTableInfo.getPropertyAndColumnMap();
            for (HashMap<String, Object> entity : entityList)
            {
                HashMap<String, Object> dbEntity = getDBEntity(propertyAndColumnMap, entity);
                mapper.updateMapById(dbEntity, tableName);
            }
            sqlSession.commit();
            //清理缓存，防止溢出
            sqlSession.clearCache();
            return true;
        } catch (Exception e) {
            //异常回滚
            sqlSession.rollback();
            return false;
        } finally {
            sqlSession.close();
        }
    }

    @Override
    public HashMap<String, Object> getMapById(Serializable id, String tableName)
    {
        CommonTableInfo commonTableInfo = getCommonTableInfoByTableName(tableName);
        HashMap<String, String> propertyAndColumnMap = commonTableInfo.getPropertyAndColumnMap();
        return commonMapper.getMapById(id, propertyAndColumnMap, tableName);
    }

    @Override
    public List<HashMap<String, Object>> getMapList(HashMap<String, Object> entity, String tableName)
    {
        CommonTableInfo commonTableInfo = getCommonTableInfoByTableName(tableName);
        HashMap<String, String> propertyAndColumnMap = commonTableInfo.getPropertyAndColumnMap();
        return commonMapper.getMapList(getDBEntity(propertyAndColumnMap, entity), propertyAndColumnMap, tableName);
    }

    @Override
    public IPage<Map<String, Object>> getMapsPage(Page<?> page, HashMap<String, Object> entity, String tableName)
    {
        CommonTableInfo commonTableInfo = getCommonTableInfoByTableName(tableName);
        HashMap<String, String> propertyAndColumnMap = commonTableInfo.getPropertyAndColumnMap();
        return commonMapper.getMapsPage(page, getDBEntity(propertyAndColumnMap, entity), propertyAndColumnMap, tableName);
    }


    /**
     * 获取数据操作对象（过滤掉非法的参数）
     *
     * @param propertyAndColumnMap
     * @param requestEntity
     * @return java.util.HashMap<java.lang.String, java.lang.Object>
     */
    private HashMap<String, Object> getDBEntity(HashMap<String, String> propertyAndColumnMap, HashMap<String, Object> requestEntity)
    {
        HashMap<String, Object> dbEntity = new HashMap<>();
        if(requestEntity==null || requestEntity.size()==0)
        {
            return dbEntity;
        }
        for (String key : requestEntity.keySet())
        {
            String column = propertyAndColumnMap.get(key);
            if (!StringUtils.isEmpty(column))
            {
                dbEntity.put(column, requestEntity.get(key));
            }
            else
            {
                log.error(String.format("尝试解析通用请求，未知的参数: %s = %s", key, requestEntity.get(key)));
            }
        }
        return dbEntity;
    }

    /**
     * 获取数据库信息
     *
     * @param tableName
     * @return com.candy.common.entity.CommonTableInfo
     */
    private CommonTableInfo getCommonTableInfoByTableName(String tableName)
    {
        CommonTableInfo commonTableInfo = TABLE_CACHE.get(tableName);
        if (commonTableInfo != null)
        {
            return commonTableInfo;
        }
        ResultSet rs = null;
        Connection connection = null;
        try
        {
            connection = dataSource.getConnection();
            DatabaseMetaData metaData = connection.getMetaData();
            rs = metaData.getColumns(connection.getCatalog(), connection.getSchema(), tableName, "%");
            List<String> columnList = new ArrayList<>();
            HashMap<String, String> propertyAndColumnMap = new HashMap<>();
            while (rs.next())
            {
                String columnName = rs.getString("COLUMN_NAME");
                columnList.add(columnName);
                //下划线转驼峰
                String property = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, columnName);
                propertyAndColumnMap.put(property, columnName);
            }
            if (columnList.size() > 0)
            {
                commonTableInfo = new CommonTableInfo();
                commonTableInfo.setColumnList(columnList);
                commonTableInfo.setPropertyAndColumnMap(propertyAndColumnMap);
                TABLE_CACHE.put(tableName, commonTableInfo);
            }
        } catch (SQLException throwables)
        {
            throwables.printStackTrace();
        }
        finally
        {
            try
            {
                if(connection!=null)
                {
                    connection.close();
                }
                if(rs!=null)
                {
                    rs.close();
                }
            }
            catch (SQLException e)
            {
                e.printStackTrace();
            }
        }
        if (commonTableInfo == null)
        {
            throw new BusinessException(String.format("您请求的对象不存在，%s", tableName));
        }
        return commonTableInfo;
    }

}
